
#pragma once


#include <Wire.h>

#include "oled_interface.hpp"
#include "oled_common.hpp"


namespace oled_basic {


    /**
     * @brief 基于Wire 库，适用于I2C 接口的SSD1306、SSD1316 等兼容型号OLED 屏幕。
     *
     * @tparam Driver         要和backend 组合的driver 类
     * @tparam &WireInstance  某些芯片有多个Wire 实例，对应多个I2C 外设，比如328PB，用这个参数选择要用哪个
     */
    template <typename Driver, TwoWire &WireInstance = Wire>
    class BackendWire {
       public:
        // static constexpr size_t BUFFER_SIZE = TWI_BUFFER_SIZE;

        // static_assert(BUFFER_SIZE < 256);

        static constexpr int8_t OK = 0;
        static constexpr int8_t ERR_BUFFER_OVERFLOW = 1;
        static constexpr int8_t ERR_NACK_AFTER_ADDRESS = 2;
        static constexpr int8_t ERR_NACK_AFTER_DATA = 3;
        static constexpr int8_t ERR_BUS_ERROR = 4;
        static constexpr int8_t ERR_TIMEOUT = 5;

       private:
        // uint8_t _data_counter = 0;
        int8_t _last_error = 0;
        int8_t _address = 0x3c;

        // 禁止拷贝
        _MAKE__OBJECT__NOT__COPYABLE(BackendWire)

       public:
        BackendWire(uint8_t address = 0x3c) :
            _address(address) {}

        void begin_tx(txmode m = txmode::data) {
            WireInstance.beginTransmission(_address);

            if (m == txmode::cmd) {
                WireInstance.write(0x00);
            }
            else {
                WireInstance.write(0x40);
            }

            // _data_counter = 0;
        }


        /**
         * @brief 将写入缓冲区的数据发送给从机
         *
         * 返回值：
         * 0 .. success
         * 1 .. length too long for buffer
         * 2 .. address send, NACK received
         * 3 .. data send, NACK received
         * 4 .. other twi error (lost bus arbitration, bus error, ..)
         * 5 .. timeout
         *
         * @return int_fast8_t
         */
        int_fast8_t end() {
            _last_error = WireInstance.endTransmission();
            return _last_error;
        }


        void write(uint_fast8_t d) {
            auto ret = WireInstance.write(d);
            
            if (ret != 1) {
                // Wire 内部缓冲区已写满，写入失败
                // 将缓冲区内现有数据发送走，不发送STOP，重复发送START 信号，
                // 所以不会在中途释放总线
                _last_error = WireInstance.endTransmission(false);
                if(_last_error == ERR_BUFFER_OVERFLOW) {
                    _last_error = 0;
                }

                // 默认缓冲区只可能在发送数据时被写满，如果考虑发送指令时的情况，那就得还得存储当前的传输模式
                begin_tx(txmode::data);

                WireInstance.write(d);
            }
        }

        void write(uint8_t *data, size_t len) {
            for (; len > 0; --len) {
                uint8_t d = *data;
                ++data;
                write(d);
            }
        }

        int_fast8_t last_error() {
            return _last_error;
        }

        void enable_high_frequency() {
            WireInstance.setClock(HIGH_SPEED_I2C_FREQUENCY);
        }

        void enable_low_frequency() {
            WireInstance.setClock(LOW_SPEED_I2C_FREQUENCY);
        }

        void init(bool use_high_speed = false) {
            if (use_high_speed) {
                // 将默认的I2C 频率从100kHz 提高到400kHz，大部分设备都支持
                WireInstance.setClock(HIGH_SPEED_I2C_FREQUENCY);
            }
            else {
                WireInstance.setClock(LOW_SPEED_I2C_FREQUENCY);
            }
        }
    };

}  // namespace oled_basic
